/*
Copyright © 2024 weidongkl <weidongkx@gmail.com>
*/

package similarity

import (
	"container/heap"
	"sync"
)

// Calculator defines an interface for calculating similarity.
type Calculator interface {
	Calculate(s1, s2 string) (float64, error)
	Name() string
}

// Calculate calculates the similarity between two strings using the provided Calculator.
func Calculate(calc Calculator, s1, s2 string) (float64, error) {
	return calc.Calculate(s1, s2)
}

// CalculateArray computes the priority of each string in the array relative to a given string using a provided
// calculation rule, and returns the strings sorted by their priorities.
// calc: An object implementing the Calculator interface used to calculate the priority between two strings.
// s1: The base string against which priorities are calculated.
// sArr: An array of strings whose priorities need to be calculated.
// Returns: A sorted array of strings by priority and any potential errors encountered.
func CalculateArray(calc Calculator, s1 string, sArr []string) ([]Item, error) {
	var (
		pq  PriorityQueue
		pa  []Item
		mu  sync.Mutex
		wg  sync.WaitGroup
		err error
	)
	heap.Init(&pq)
	results := make(chan *Item, len(sArr))

	for _, s2 := range sArr {
		wg.Add(1)
		go func(s2 string) {
			defer wg.Done()
			prio, e := calc.Calculate(s1, s2)
			if e != nil {
				mu.Lock()
				if err == nil {
					err = e
				}
				mu.Unlock()
				return
			}
			results <- &Item{
				Value:    s2,
				Priority: prio,
			}
		}(s2)
	}

	wg.Wait()
	close(results)

	if err != nil {
		return nil, err
	}

	for item := range results {
		heap.Push(&pq, item)
	}

	for pq.Len() > 0 {
		item := heap.Pop(&pq).(*Item)
		pa = append(pa, *item)
	}
	return pa, nil
}
